﻿using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using Binder = Microsoft.CSharp.RuntimeBinder.Binder;

namespace EBA.Dynamic
{
    /// <summary>
    /// 动态类生成器
    /// </summary>
    public static class DynamicTypeBuilder
    {
        /// <summary>
        /// 是否保存动态代码到磁盘。for debugging
        /// </summary>
        private static bool _IsPersistented = false;

        private static ModuleBuilder _ModuleBuilder;
        private static AssemblyBuilder _AssemblyBuilder;
        ///<summary>
        /// Module Builder for buiding proxies
        ///</summary>
        static internal ModuleBuilder ModuleBuilder
        {
            get
            {
                if (_ModuleBuilder == null)
                {
                    var assemblyBuilderAccess = _IsPersistented ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run;
                    var assemblyName = "EbaDynamicAssembly.dll";
                    _AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), assemblyBuilderAccess);

                    _ModuleBuilder = _AssemblyBuilder.DefineDynamicModule("EbaDynamicAssembly.dll");
                }
                return _ModuleBuilder;
            }
        }

        internal static Type BuildType(Type targetType, Type dynamicContextType)
        {
            if (targetType.IsInterface == false)
            {
                throw new NotSupportedException("Only supports dynamically cast to an interface.");
            }

            if (dynamicContextType.GetInterfaces().Any(i => i == typeof(IDynamicContext)) == false)
            {
                throw new NotSupportedException("The dynamic context must implement IDynamicContext.");
            }

            var typeBuilder = ModuleBuilder.DefineType(
                string.Format("DynamicProxy_{0}", targetType.AssemblyQualifiedName.Replace(".", "_").Replace(",", "_")), TypeAttributes.Public | TypeAttributes.Class,
                dynamicContextType, new Type[] { targetType });

            typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(SerializableAttribute).GetConstructor(Type.EmptyTypes), new object[] { }));

            var callsiteFields = new Dictionary<Type, FieldBuilder>();


            DefineProperties(targetType, dynamicContextType, typeBuilder, callsiteFields);

            DefineMethods(targetType, dynamicContextType, typeBuilder, callsiteFields);

            var dynamicType = typeBuilder.CreateType();

            if (_IsPersistented)
            {
                _AssemblyBuilder.Save("EbaDynamicAssembly.dll");
            }

            return dynamicType;
        }


        #region DefineProperties

        /// <summary>
        /// 获取包括父接口在内的属性列表
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static List<PropertyEmitData> GetPropertyEmitDatas(Type type)
        {
            var properties = new List<PropertyEmitData>();

            properties.AddRange(type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(it => !it.IsSpecialName)
                .Select(i => new PropertyEmitData(type, i)));

            foreach (var parent in type.GetInterfaces())
            {
                properties.AddRange(GetPropertyEmitDatas(parent));
            }

            return properties;
        }

        private static void DefineProperties(Type targetType, Type dynamicContextType, TypeBuilder typeBuilder, Dictionary<Type, FieldBuilder> callsiteFields)
        {
            foreach (var item in GetPropertyEmitDatas(targetType))
            {
                var property = item.PropertyInfo;

                var propertyBuilder = typeBuilder.DefineProperty(item.Prefix + "." + property.Name, PropertyAttributes.None, property.PropertyType, new Type[] { property.PropertyType });

                if (property.CanRead)//get_
                {
                    var getterBuidler = typeBuilder.DefineMethod("get_" + property.Name,
                    MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
                    property.PropertyType, null);

                    var il = getterBuidler.GetILGenerator();

                    var callsiteGenericElementType = BuildCallsiteGenericElementType(null, true);
                    var callsiteField = BuildCallsiteField(typeBuilder, callsiteGenericElementType, callsiteFields);


                    LocalBuilder val = il.DeclareLocal(typeof(object));
                    LocalBuilder ret = il.DeclareLocal(property.PropertyType);
                    LocalBuilder cshaprArgumentInfos = il.DeclareLocal(typeof(CSharpArgumentInfo[]));


                    var callsiteFieldIsInitialized = il.DefineLabel();

                    il.Emit(OpCodes.Nop);
                    il.Emit(OpCodes.Ldsfld, callsiteField);
                    il.Emit(OpCodes.Brtrue_S, callsiteFieldIsInitialized);

                    //CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Name", this.ContextType, new CSharpArgumentInfo[]
                    //{
                    //    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                    //    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    //}));
                    il.Ldc_I4(0);//CSharpBinderFlags.None
                    il.Emit(OpCodes.Ldstr, property.Name);

                    il.Emit(OpCodes.Ldarg_0);//this.
                    il.Emit(OpCodes.Call, dynamicContextType.GetMethod("get_ContextType", BindingFlags.Public | BindingFlags.Instance));

                    //cshaprArgumentInfos = new CshaprArgumentInfos[1];
                    il.Newarr(typeof(CSharpArgumentInfo), 2);
                    il.Emit(OpCodes.Stloc_S, cshaprArgumentInfos);

                    //cshaprArgumentInfos[0]= CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    il.Emit(OpCodes.Ldloc_S, cshaprArgumentInfos);
                    il.Emit(OpCodes.Ldc_I4_0);

                    // = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    il.Emit(OpCodes.Ldc_I4_0);
                    il.Emit(OpCodes.Ldnull);
                    il.Emit(OpCodes.Call, typeof(CSharpArgumentInfo).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Stelem_Ref);


                    il.Emit(OpCodes.Ldloc_S, cshaprArgumentInfos);

                    il.Emit(OpCodes.Call, typeof(Binder).GetMethod("GetMember", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Call, typeof(CallSite<>).MakeGenericType(callsiteGenericElementType).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Stsfld, callsiteField);//Invoke_TestMethod = 

                    il.MarkLabel(callsiteFieldIsInitialized);


                    il.Emit(OpCodes.Ldsfld, callsiteField);
                    il.Emit(OpCodes.Ldfld, typeof(CallSite<>).MakeGenericType(callsiteGenericElementType).GetField("Target", BindingFlags.Public | BindingFlags.Instance));
                    il.Emit(OpCodes.Ldsfld, callsiteField);
                    il.Emit(OpCodes.Ldarg_0);//this.
                    il.Emit(OpCodes.Call, dynamicContextType.GetMethod("get_ContextObject", BindingFlags.Public | BindingFlags.Instance));


                    il.Emit(OpCodes.Callvirt, callsiteGenericElementType.GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance));


                    il.Emit(OpCodes.Stloc_S, val);
                    //il.Emit(OpCodes.Ldloc_S, val);

                    //il.Emit(OpCodes.Ldtoken, property.PropertyType);
                    //il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Public | BindingFlags.Static));

                    //il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }));
                    //il.Emit(OpCodes.Castclass, property.PropertyType);
                    //il.Emit(OpCodes.Stloc_S, ret);


                    //CallSite<Func<CallSite, object, Judge>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(Judge), typeof(dyDataObject)));
                    var valueLabel = il.DefineLabel();

                    il.Ldc_I4(0);//CSharpBinderFlags.None
                    il.Emit(OpCodes.Ldtoken, property.PropertyType);
                    il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Ldarg_0);//this.
                    il.Emit(OpCodes.Call, dynamicContextType.GetMethod("get_ContextType", BindingFlags.Public | BindingFlags.Instance));

                    il.Emit(OpCodes.Call, typeof(Binder).GetMethod("Convert", BindingFlags.Public | BindingFlags.Static));

                    var enmuCallSite = GetEnumCallSite(null, true, property.PropertyType);
                    //var enmuCallSite = BuildCallsiteGenericElementType(null, true, property.PropertyType);
                    var callsiteFieldEnum = BuildCallsiteField(typeBuilder, enmuCallSite, callsiteFields);
                    il.Emit(OpCodes.Call, typeof(CallSite<>).MakeGenericType(enmuCallSite).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Stsfld, callsiteFieldEnum);
                    il.MarkLabel(valueLabel);

                    var convertTypeLabel = il.DefineLabel();
                    il.Emit(OpCodes.Ldsfld, callsiteFieldEnum);
                    il.Emit(OpCodes.Ldfld, typeof(CallSite<>).MakeGenericType(enmuCallSite).GetField("Target", BindingFlags.Public | BindingFlags.Instance));
                    il.Emit(OpCodes.Ldsfld, callsiteFieldEnum);
                    il.Emit(OpCodes.Ldloc_S, val);
                    il.Emit(OpCodes.Callvirt, enmuCallSite.GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance));
                    il.Emit(OpCodes.Stloc_S, ret);
                    il.MarkLabel(convertTypeLabel);
                    //

                    var returnLable = il.DefineLabel();
                    il.Emit(OpCodes.Br_S, returnLable);
                    il.MarkLabel(returnLable);

                    il.Emit(OpCodes.Ldloc_S, ret);
                    il.Ret();


                    var getterMethod = targetType.GetMethod("get_" + property.Name);
                    typeBuilder.DefineMethodOverride(getterBuidler, item.GetMethod);

                    propertyBuilder.SetGetMethod(getterBuidler);
                }

                if (property.CanWrite)
                {
                    var setterBuidler = typeBuilder.DefineMethod("set_" + property.Name,
                MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
                null, new Type[] { property.PropertyType });

                    var il = setterBuidler.GetILGenerator();

                    var callsiteGenericElementType = BuildCallsiteGenericElementType(new Type[] { property.PropertyType }, false);
                    var callsiteField = BuildCallsiteField(typeBuilder, callsiteGenericElementType, callsiteFields);


                    LocalBuilder cshaprArgumentInfos = il.DeclareLocal(typeof(CSharpArgumentInfo[]));


                    var callsiteFieldIsInitialized = il.DefineLabel();

                    il.Emit(OpCodes.Nop);
                    il.Emit(OpCodes.Ldsfld, callsiteField);
                    il.Emit(OpCodes.Brtrue_S, callsiteFieldIsInitialized);

                    //CallSite<Func<CallSite, object, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "Name", this.ContextType, new CSharpArgumentInfo[]
                    //{
                    //    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    //}));
                    il.Ldc_I4(0);//CSharpBinderFlags.None
                    il.Emit(OpCodes.Ldstr, property.Name);

                    il.Emit(OpCodes.Ldarg_0);//this.
                    il.Emit(OpCodes.Call, dynamicContextType.GetMethod("get_ContextType", BindingFlags.Public | BindingFlags.Instance));

                    //cshaprArgumentInfos = new CshaprArgumentInfos[1];
                    il.Newarr(typeof(CSharpArgumentInfo), 2);
                    il.Emit(OpCodes.Stloc_S, cshaprArgumentInfos);

                    //cshaprArgumentInfos[0]= CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    il.Emit(OpCodes.Ldloc_S, cshaprArgumentInfos);
                    il.Emit(OpCodes.Ldc_I4_0);

                    // = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    il.Emit(OpCodes.Ldc_I4_0);
                    il.Emit(OpCodes.Ldnull);
                    il.Emit(OpCodes.Call, typeof(CSharpArgumentInfo).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Stelem_Ref);

                    //cshaprArgumentInfos[1]= CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    il.Emit(OpCodes.Ldloc_S, cshaprArgumentInfos);
                    il.Emit(OpCodes.Ldc_I4_1);

                    // = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    il.Emit(OpCodes.Ldc_I4_1);
                    il.Emit(OpCodes.Ldnull);
                    il.Emit(OpCodes.Call, typeof(CSharpArgumentInfo).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Stelem_Ref);



                    il.Emit(OpCodes.Ldloc_S, cshaprArgumentInfos);

                    il.Emit(OpCodes.Call, typeof(Binder).GetMethod("SetMember", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Call, typeof(CallSite<>).MakeGenericType(callsiteGenericElementType).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));
                    il.Emit(OpCodes.Stsfld, callsiteField);//Invoke_TestMethod = 

                    il.MarkLabel(callsiteFieldIsInitialized);


                    il.Emit(OpCodes.Ldsfld, callsiteField);
                    il.Emit(OpCodes.Ldfld, typeof(CallSite<>).MakeGenericType(callsiteGenericElementType).GetField("Target", BindingFlags.Public | BindingFlags.Instance));
                    il.Emit(OpCodes.Ldsfld, callsiteField);
                    il.Emit(OpCodes.Ldarg_0);//this.
                    il.Emit(OpCodes.Call, dynamicContextType.GetMethod("get_ContextObject", BindingFlags.Public | BindingFlags.Instance));

                    il.Emit(OpCodes.Ldarg_1);//value
                    il.Emit(OpCodes.Callvirt, callsiteGenericElementType.GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance));


                    il.Emit(OpCodes.Nop);
                    il.Ret();


                    typeBuilder.DefineMethodOverride(setterBuidler, item.SetMethod);

                    propertyBuilder.SetSetMethod(setterBuidler);
                }


            }
        }

        private static Type GetEnumCallSite(Type[] parameterTypes, bool hasReturnValue, Type enumtype)
        {
            Type type;
            var typeArguments = new List<Type>();
            typeArguments.Add(typeof(CallSite));
            typeArguments.Add(typeof(object));

            if (parameterTypes != null && parameterTypes.Length > 0)
            {
                typeArguments.AddRange(parameterTypes);
            }

            if (hasReturnValue)
            {
                //callsite 默认都返回object，需要自行做类型转换

                if (enumtype.IsEnum)
                {
                    typeArguments.Add(enumtype);
                }
                else
                {
                    typeArguments.Add(typeof(object));
                }
                type = Expression.GetFuncType(typeArguments.ToArray());
            }
            else
            {
                type = Expression.GetActionType(typeArguments.ToArray());
            }
            return type;
        }
        #endregion

        /// <summary>
        /// 生成<see cref="CallSite{T}"/>的泛型类型。
        /// </summary>
        /// <param name="parameterTypes"></param>
        /// <param name="hasReturnValue"></param>
        /// <returns></returns>
        //static Type BuildCallsiteGenericElementType(Type[] parameterTypes, bool hasReturnValue, Type targetType)
        static Type BuildCallsiteGenericElementType(Type[] parameterTypes, bool hasReturnValue = false)
        {
            Type type;
            var typeArguments = new List<Type>();
            typeArguments.Add(typeof(CallSite));
            typeArguments.Add(typeof(object));

            if (parameterTypes != null && parameterTypes.Length > 0)
            {
                typeArguments.AddRange(parameterTypes);
            }

            if (hasReturnValue)
            {
                //if (targetType.IsEnum)
                //{
                //    typeArguments.Add(targetType);
                //}
                //else
                //{
                //    typeArguments.Add(typeof(object));//callsite 默认都返回object，需要自行做类型转换
                //}
                typeArguments.Add(typeof(object));//callsite 默认都返回object，需要自行做类型转换
                type = Expression.GetFuncType(typeArguments.ToArray());
            }
            else
            {
                type = Expression.GetActionType(typeArguments.ToArray());
            }

            return type;
        }




        /// <summary>
        /// 生成<see cref="CallSite{T}"/>的变量
        /// </summary>
        /// <param name="typeBuilder"></param>
        /// <param name="callsiteGenericElementType"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        static FieldBuilder BuildCallsiteField(TypeBuilder typeBuilder, Type callsiteGenericElementType, Dictionary<Type, FieldBuilder> fields)
        {
            //Callsite 委托有缓存，所以即使是相同类型的Callsite也不可复用
            //FieldBuilder field;
            //if (fields.ContainsKey(callsiteGenericElementType) == false)
            //{
            return typeBuilder.DefineField("Invoke_" + Guid.NewGuid().ToString("N"), typeof(CallSite<>).MakeGenericType(callsiteGenericElementType), FieldAttributes.Private | FieldAttributes.Static);
            //    fields.Add(callsiteGenericElementType, field);
            //}
            //else
            //{
            //    field = fields[callsiteGenericElementType];
            //}

            //return field;
        }

        #region DefineMethod
        private static void DefineMethods(Type targetType, Type dynamicContextType, TypeBuilder typeBuilder, Dictionary<Type, FieldBuilder> fields)
        {
            foreach (var data in GetMethodEmitDatas(targetType))
            {
                var methodBuilder = typeBuilder.DefineMethod(data.Prefix + "." + data.Method.Name,
                    MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
                    data.Method.ReturnType,
                    data.ParameterTypes
               );


                DefineMethod(typeBuilder, fields, data, methodBuilder, dynamicContextType);

                typeBuilder.DefineMethodOverride(methodBuilder, data.Method);
            }
        }

        private static void DefineMethod(TypeBuilder typeBuilder, Dictionary<Type, FieldBuilder> fields, MethodEmitData data, MethodBuilder methodBuilder, Type contextType)
        {
            var il = methodBuilder.GetILGenerator();

            FieldBuilder callsiteField = BuildCallsiteField(typeBuilder, data.CallSiteGenericElementType, fields);

            var callsiteFieldIsInitialized = il.DefineLabel();


            for (var i = 0; i < data.Parameters.Length; i++)
            {
                var p = data.Parameters[i];
                var args = methodBuilder.DefineParameter(i + 1, p.Attributes, p.Name);

            }

            //bool result;
            var result = il.DeclareLocal(typeof(bool));
            LocalBuilder retObj = null;
            LocalBuilder convertedObj = null;
            if (data.Method.ReturnType != typeof(void))
            {
                retObj = il.DeclareLocal(typeof(object));
                convertedObj = il.DeclareLocal(data.Method.ReturnType);
            }

            //if(this.Field == null){}else{goto}
            il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldsfld, callsiteField);
            il.Emit(OpCodes.Ldnull);
            il.Emit(OpCodes.Ceq);
            il.Emit(OpCodes.Ldc_I4_0);//false
            il.Emit(OpCodes.Ceq);
            il.Emit(OpCodes.Stloc_S, result);
            il.Emit(OpCodes.Ldloc_S, result);//result
            il.Emit(OpCodes.Brtrue_S, callsiteFieldIsInitialized);
            il.Nop();

            //Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "TestMethod", null, this.ContextType, new CSharpArgumentInfo[]{
            //    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
            //})

            il.Ldc_I4((int)data.CSharpBinderFlags);
            il.Ldstr(data.Method.Name);

            if (data.Method.IsGenericMethod)
            {
            }
            else
            {
                // Type.EmptyTypes
                // il.Emit(OpCodes.Ldsfld, typeof(Type).GetField("EmptyTypes", BindingFlags.Public | BindingFlags.Static));
                il.Emit(OpCodes.Ldnull);
            }


            il.Emit(OpCodes.Ldarg_0);//this.
            il.Emit(OpCodes.Call, contextType.GetMethod("get_ContextType", BindingFlags.Public | BindingFlags.Instance));
            //il.Ldfld(typeof(MockObject).GetField("ContextType", BindingFlags.Instance | BindingFlags.NonPublic));

            var cshaprArgumentInfos = il.DeclareLocal(typeof(CSharpArgumentInfo[]));

            il.Newarr(typeof(CSharpArgumentInfo), data.CSharpArgumentInfos.Count);
            il.Emit(OpCodes.Stloc_S, cshaprArgumentInfos);

            var arrayIndex = 0;
            foreach (var item in data.CSharpArgumentInfos)
            {
                il.Emit(OpCodes.Ldloc_S, cshaprArgumentInfos);
                il.Ldc_I4(arrayIndex);
                arrayIndex++;

                il.Ldc_I4((int)item.Key);
                var value = item.Value;
                if (value == null)
                {

                    il.Ldnull();
                }
                else
                {
                    il.Ldstr(value);
                }


                il.Emit(OpCodes.Call, typeof(CSharpArgumentInfo).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));

                il.Stelem_Ref();
            }

            il.Ldloc(cshaprArgumentInfos);
            il.Emit(OpCodes.Call, typeof(Binder).GetMethod("InvokeMember", BindingFlags.Public | BindingFlags.Static));

            //Invoke_TestMethod = CallSite<Action<CallSite, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "TestMethod", Type.EmptyTypes, this.ContextType, new CSharpArgumentInfo[]{
            //    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
            //    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsRef,null)
            //}));
            il.Emit(OpCodes.Call, typeof(CallSite<>).MakeGenericType(data.CallSiteGenericElementType).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));
            il.Emit(OpCodes.Stsfld, callsiteField);//Invoke_TestMethod = 

            il.Nop();

            il.MarkLabel(callsiteFieldIsInitialized);


            il.Emit(OpCodes.Ldsfld, callsiteField);
            il.Emit(OpCodes.Ldfld, typeof(CallSite<>).MakeGenericType(data.CallSiteGenericElementType).GetField("Target", BindingFlags.Public | BindingFlags.Instance));
            il.Emit(OpCodes.Ldsfld, callsiteField);
            il.Emit(OpCodes.Ldarg_0);//this.
            il.Emit(OpCodes.Call, contextType.GetMethod("get_ContextObject", BindingFlags.Public | BindingFlags.Instance));

            // il.Ldfld(typeof(MockObject).GetField("ContextObject", BindingFlags.Instance | BindingFlags.NonPublic));


            for (var i = 0; i < data.Parameters.Length; i++)
            {
                il.Emit(OpCodes.Ldarg_S, i + 1);
            }

            il.Emit(OpCodes.Callvirt, data.CallSiteGenericElementType.GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance));

            if (data.Method.ReturnType == typeof(void))
            {
                il.Nop();
                il.Ret();
            }
            else
            {

                il.Emit(OpCodes.Stloc_S, retObj);
                //il.Emit(OpCodes.Ldloc_S, retObj);

                //il.Emit(OpCodes.Ldtoken, data.Method.ReturnType);
                //il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Public | BindingFlags.Static));

                //il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }));
                //il.Emit(OpCodes.Castclass, data.Method.ReturnType);
                //il.Emit(OpCodes.Stloc_S, convertedObj);



                //var returnLable = il.DefineLabel();
                //il.Emit(OpCodes.Br_S, returnLable);
                //il.MarkLabel(returnLable);
                //il.Emit(OpCodes.Ldloc_S, convertedObj);
                //il.Ret();




                //CallSite<Func<CallSite, object, Judge>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(Judge), typeof(dyDataObject)));
                var valueLabel = il.DefineLabel();

                il.Ldc_I4(0);//CSharpBinderFlags.None
                il.Emit(OpCodes.Ldtoken, data.Method.ReturnType);
                il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Public | BindingFlags.Static));
                il.Emit(OpCodes.Ldarg_0);//this.
                il.Emit(OpCodes.Call, contextType.GetMethod("get_ContextType", BindingFlags.Public | BindingFlags.Instance));

                il.Emit(OpCodes.Call, typeof(Binder).GetMethod("Convert", BindingFlags.Public | BindingFlags.Static));

                var enmuCallSite = GetEnumCallSite(null, true, data.Method.ReturnType);
                //var enmuCallSite = BuildCallsiteGenericElementType(null, true, property.PropertyType);
                var callsiteFieldEnum = BuildCallsiteField(typeBuilder, enmuCallSite, fields);
                il.Emit(OpCodes.Call, typeof(CallSite<>).MakeGenericType(enmuCallSite).GetMethod("Create", BindingFlags.Public | BindingFlags.Static));
                il.Emit(OpCodes.Stsfld, callsiteFieldEnum);
                il.MarkLabel(valueLabel);

                var convertTypeLabel = il.DefineLabel();
                il.Emit(OpCodes.Ldsfld, callsiteFieldEnum);
                il.Emit(OpCodes.Ldfld, typeof(CallSite<>).MakeGenericType(enmuCallSite).GetField("Target", BindingFlags.Public | BindingFlags.Instance));
                il.Emit(OpCodes.Ldsfld, callsiteFieldEnum);
                il.Emit(OpCodes.Ldloc_S, retObj);
                il.Emit(OpCodes.Callvirt, enmuCallSite.GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance));
                il.Emit(OpCodes.Stloc_S, convertedObj);
                il.MarkLabel(convertTypeLabel);


                var returnLable = il.DefineLabel();
                il.Emit(OpCodes.Br_S, returnLable);
                il.MarkLabel(returnLable);

                il.Emit(OpCodes.Ldloc_S, convertedObj);
                il.Ret();
            }
        }



        /// <summary>
        /// 获取包括父接口在内的方法列表
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static List<MethodEmitData> GetMethodEmitDatas(Type type)
        {
            var methods = new List<MethodEmitData>();

            methods.AddRange(type.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(it => !it.IsSpecialName)
                .Select(i => new MethodEmitData(type, i)));

            foreach (var parent in type.GetInterfaces())
            {
                methods.AddRange(GetMethodEmitDatas(parent));
            }

            return methods;
        }
        #endregion
    }
}
